﻿Imports System.Reflection
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized

Namespace Save

    ''' <summary>
    ''' テーブルアーカイブクラス
    ''' </summary>
    ''' <remarks>
    ''' #107 Storageクラスの見直し
    ''' </remarks>
    Public Class TableArchive
        Implements ISaveDefinitionFactory, IArchive

#Region "プロパティ"
        Private _modelKey As String
        Public Property ModelKey() As String
            Get
                Return _modelKey
            End Get
            Friend Set(ByVal value As String)
                _modelKey = value
            End Set
        End Property

        Public ReadOnly Property Key As String Implements IArchive.Key
            Get
                Return _modelKey
            End Get
        End Property

        Private _typeFullName As String
        Public Property TypeFullName() As String
            Get
                Return _typeFullName
            End Get
            Friend Set(ByVal value As String)
                _typeFullName = value
            End Set
        End Property

        Private _columnArchives As Dictionary(Of String, ColumnArchive)

        Public ReadOnly Property ColumnArchives As Dictionary(Of String, ColumnArchive)
            Get
                If _columnArchives Is Nothing Then _columnArchives = New Dictionary(Of String, ColumnArchive)
                Return _columnArchives
            End Get
        End Property

        Private _childListArchives As Dictionary(Of String, ListArchive)
        ''' <summary>
        ''' 子ナリストアーカイブ
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ChildListArchives As Dictionary(Of String, ListArchive)
            Get
                If _childListArchives Is Nothing Then _childListArchives = New Dictionary(Of String, ListArchive)
                Return _childListArchives
            End Get
        End Property

        Private _superTableArchive As TableArchive
        ''' <summary>
        ''' 基底テーブルアーカイブテーブル
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property SuperTableArchive As TableArchive
            Get
                Return _superTableArchive
            End Get
            Friend Set(value As TableArchive)
                _superTableArchive = value
            End Set
        End Property
#End Region

#Region "クエリ"
        ''' <summary>
        ''' ダーティな列を取得します
        ''' </summary>
        ''' <param name="instance"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function GetDirtyColumns(instance As Object) As IEnumerable(Of ColumnArchive)
            Return (From el In Me.ColumnArchives.Select(Function(kv) kv.Value) Where el.IsDirty(instance))
        End Function

#End Region

        ''' <summary>
        ''' ダーティかどうかを示します
        ''' </summary>
        ''' <param name="instance">検査対象インスタンス</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function IsDirty(instance As Object) As Boolean Implements IArchive.IsDirty
            '列チェック
            If Me.GetDirtyColumns(instance).Any Then Return True

            '基底チェック
            If Me.SuperTableArchive IsNot Nothing AndAlso Me.SuperTableArchive.IsDirty(instance) Then Return True

            '子チェック
            If Me.ChildListArchives IsNot Nothing AndAlso Me.ChildListArchives.Any Then
                For Each arch In Me.ChildListArchives.Select(Function(x) x.Value)
                    '検査対象のリストを取得
                    Dim prop As PropertyInfo = instance.GetType.GetProperty(arch.PropertyName)
                    Dim lst As ICollection = prop.GetValue(instance, Nothing)

                    If arch.IsDirty(lst) Then Return True
                Next
            End If

            Return False
        End Function

#Region "ISaveDefinitionFactory 実装"
        ''' <summary>
        ''' 保存定義を作成します
        ''' </summary>
        ''' <param name="instance"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CreateSaveDefinitions(instance As Object) As System.Collections.Generic.List(Of SaveDefinition) Implements ISaveDefinitionFactory.CreateSaveDefinitions
            Dim defs As New List(Of SaveDefinition)

            '列チェック（列更新ダーティ時のみ、更新定義生成）
            If Me.GetDirtyColumns(instance).Any Then
                Dim def As New SaveDefinition With {
                    .SaveType = SaveType.Update,
                    .TableArchive = Me,
                    .Model = instance,
                    .TableAttribute = Me.GetTableAttribute
                }
                defs.Add(def)
            End If

            '基底チェック
            If Me.SuperTableArchive IsNot Nothing Then
                defs.AddRange(Me.SuperTableArchive.CreateSaveDefinitions(instance))
            End If

            '子チェック
            If Me.ChildListArchives IsNot Nothing AndAlso Me.ChildListArchives.Any Then
                For Each item In Me.ChildListArchives.Select(Function(x) x.Value)
                    Dim prop As PropertyInfo = instance.GetType.GetProperty(item.PropertyName)
                    Dim lst As ICollection = prop.GetValue(instance, Nothing)
                    defs.AddRange(item.CreateSaveDefinitions(lst))
                Next
            End If

            Return defs
        End Function

        ''' <summary>
        ''' 指定された保存タイプの保存定義を強制的に作成します
        ''' </summary>
        ''' <param name="instance"></param>
        ''' <param name="saveType"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function CreateSaveDefinitions(instance As Object, saveType As SaveType) As System.Collections.Generic.List(Of SaveDefinition) Implements ISaveDefinitionFactory.CreateSaveDefinitions
            Dim defs As New List(Of SaveDefinition)

            '基底チェック（優先）
            If Me.SuperTableArchive IsNot Nothing Then
                defs.AddRange(Me.SuperTableArchive.CreateSaveDefinitions(instance, saveType))
            End If

            '自テーブルは強制的に指定されたSaveタイプに変更
            Dim def As New SaveDefinition With {
                .SaveType = saveType,
                .TableArchive = Me,
                .Model = instance,
                .TableAttribute = Me.GetTableAttribute
            }
            defs.Add(def)

            '子チェック
            If Me.ChildListArchives IsNot Nothing AndAlso Me.ChildListArchives.Any Then
                For Each item In Me.ChildListArchives.Select(Function(x) x.Value)
                    Dim prop As PropertyInfo = instance.GetType.GetProperty(item.PropertyName)
                    Dim lst As ICollection = prop.GetValue(instance, Nothing)
                    defs.AddRange(item.CreateSaveDefinitions(lst, saveType))
                Next

            End If

            Return defs
        End Function
#End Region

        Public Overrides Function ToString() As String
            Return Me.Key
        End Function

        ''' <summary>
        ''' テーブル属性を取得します
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Friend Function GetTableAttribute() As TableAttribute
            Static atr As TableAttribute = Nothing

            If atr Is Nothing Then
                atr = TableAttributeReader.Read(Me.TypeFullName)
            End If

            Return atr
        End Function
    End Class

End Namespace
